#include <iostream>
#include <stdio.h>

class Base
{
	public:
		int x;
		int y;

		virtual void function_1()
		{
			printf("function_1...\n");
		}

		virtual void function_2()
		{
			printf("function_2...\n");
		}
};

int main(int argc, char ** argv)
{
	Base base;
	Base *pb;

	printf("base address is 0x%x.\n", (unsigned int)(long)&base);	// this 指针，是堆栈里面的地址。虚表的地址不是this指针，是this指针的前8个字节。
	printf("base virtual table address is:0x%x\n", (unsigned int)*(long*)&base);   // 虚表地址。取this指针的前8个字节。*(char*)&base为取一个字节。	

	typedef void(*pfunction)(void);
	
	pfunction pf;

	long temp = *((long*)(*(long*)&base) + 0);		// 取地址表里面的第一个值
							// *(long*)&base) 是虚表地址
							// (long*) 转换成一个long*类型。
							// *((long*)(*(long*)&base) + 0)， 加的是0，即取第一个8字节值。
	pf = (pfunction)*((long*)(*(long*)&base) + 0);	// 或这样，取地址表里面的第一个值
	pf();						// function_1

	pf = (pfunction)*((long*)(*(long*)&base) + 1);	// 取第二个8字节值
	pf();						// function_2

	for (int i = 0; i < 2; i++) {
		pf = (pfunction)*((long*)(*(long*)&base) + i);
		pf();
	}
}

#if 0

1.类对象的首地址里面存的第一个8字节才是虚表的地址。即this指针里面存的第一个8字节就是虚表的地址。
2.虚表的地址不是 this 指针，是 this 指针的前8个字节。
3.虚表的个数是编译的时候确定的，有几个 virtual 存几个。

int main(int argc, char ** argv)
{
    11c9:	f3 0f 1e fa          	endbr64 
    11cd:	55                   	push   %rbp
    11ce:	48 89 e5             	mov    %rsp,%rbp
    11d1:	48 83 ec 50          	sub    $0x50,%rsp
    11d5:	89 7d bc             	mov    %edi,-0x44(%rbp)
    11d8:	48 89 75 b0          	mov    %rsi,-0x50(%rbp)
    11dc:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
    11e3:	00 00 
    11e5:	48 89 45 f8          	mov    %rax,-0x8(%rbp)
    11e9:	31 c0                	xor    %eax,%eax
	Base base;
    11eb:	48 8d 45 e0          	lea    -0x20(%rbp),%rax
    11ef:	48 89 c7             	mov    %rax,%rdi
    11f2:	e8 bb 01 00 00       	callq  13b2 <_ZN4BaseC1Ev>
	Base *pb;

	printf("base address is 0x%x.\n", (unsigned int)(long)&base);	// this 指针，是堆栈里面的地址。虚表的地址不是this指针，是this指针的前四个字节。
    11f7:	48 8d 45 e0          	lea    -0x20(%rbp),%rax
    11fb:	89 c6                	mov    %eax,%esi
    11fd:	48 8d 3d 21 0e 00 00 	lea    0xe21(%rip),%rdi        # 2025 <_ZStL19piecewise_construct+0x1d>
    1204:	b8 00 00 00 00       	mov    $0x0,%eax
    1209:	e8 82 fe ff ff       	callq  1090 <printf@plt>
	printf("base virtual table address is:0x%x\n", (unsigned int)*(long*)&base);   // 虚表地址。取this指针的前4个字节。*(char*)&base为取一个字节。	
    120e:	48 8d 45 e0          	lea    -0x20(%rbp),%rax
    1212:	48 8b 00             	mov    (%rax),%rax
    1215:	89 c6                	mov    %eax,%esi
    1217:	48 8d 3d 22 0e 00 00 	lea    0xe22(%rip),%rdi        # 2040 <_ZStL19piecewise_construct+0x38>
    121e:	b8 00 00 00 00       	mov    $0x0,%eax
    1223:	e8 68 fe ff ff       	callq  1090 <printf@plt>

	typedef void(*pfunction)(void);
	
	pfunction pf;

	printf("aaa %ld\n", sizeof(int));		// 4 (64bit)
    1228:	be 04 00 00 00       	mov    $0x4,%esi
    122d:	48 8d 3d 30 0e 00 00 	lea    0xe30(%rip),%rdi        # 2064 <_ZStL19piecewise_construct+0x5c>
    1234:	b8 00 00 00 00       	mov    $0x0,%eax
    1239:	e8 52 fe ff ff       	callq  1090 <printf@plt>
	printf("aaa %ld\n", sizeof(float));		// 4 (64bit)
    123e:	be 04 00 00 00       	mov    $0x4,%esi
    1243:	48 8d 3d 1a 0e 00 00 	lea    0xe1a(%rip),%rdi        # 2064 <_ZStL19piecewise_construct+0x5c>
    124a:	b8 00 00 00 00       	mov    $0x0,%eax
    124f:	e8 3c fe ff ff       	callq  1090 <printf@plt>
	printf("aaa %ld\n", sizeof(long));		// 8 (64bit)
    1254:	be 08 00 00 00       	mov    $0x8,%esi
    1259:	48 8d 3d 04 0e 00 00 	lea    0xe04(%rip),%rdi        # 2064 <_ZStL19piecewise_construct+0x5c>
    1260:	b8 00 00 00 00       	mov    $0x0,%eax
    1265:	e8 26 fe ff ff       	callq  1090 <printf@plt>
	printf("aaa %ld\n", sizeof(double));	// 8 (64bit)
    126a:	be 08 00 00 00       	mov    $0x8,%esi
    126f:	48 8d 3d ee 0d 00 00 	lea    0xdee(%rip),%rdi        # 2064 <_ZStL19piecewise_construct+0x5c>
    1276:	b8 00 00 00 00       	mov    $0x0,%eax
    127b:	e8 10 fe ff ff       	callq  1090 <printf@plt>

	long temp = *((long*)(*(long*)&base) + 0);		// 取地址表里面的第一个值
    1280:	48 8d 45 e0          	lea    -0x20(%rbp),%rax
    1284:	48 8b 00             	mov    (%rax),%rax
    1287:	48 8b 00             	mov    (%rax),%rax
    128a:	48 89 45 d0          	mov    %rax,-0x30(%rbp)
							// *(long*)&base) 是虚表地址
							// (long*) 转换成一个int*类型。
							// *((long*)(*(long*)&base) + 0)， 加的是0，即取第一个4字节值。
	pf = (pfunction)*((long*)(*(long*)&base) + 0);	// 或这样，取地址表里面的第一个值
    128e:	48 8d 45 e0          	lea    -0x20(%rbp),%rax
    1292:	48 8b 00             	mov    (%rax),%rax
    1295:	48 8b 00             	mov    (%rax),%rax
    1298:	48 89 45 d8          	mov    %rax,-0x28(%rbp)
	pf();						// function_1
    129c:	48 8b 45 d8          	mov    -0x28(%rbp),%rax
    12a0:	ff d0                	callq  *%rax

	pf = (pfunction)*((long*)(*(long*)&base) + 1);	// 取第二个4字节值
    12a2:	48 8d 45 e0          	lea    -0x20(%rbp),%rax
    12a6:	48 8b 00             	mov    (%rax),%rax
    12a9:	48 83 c0 08          	add    $0x8,%rax
    12ad:	48 8b 00             	mov    (%rax),%rax
    12b0:	48 89 45 d8          	mov    %rax,-0x28(%rbp)
	pf();						// function_2
    12b4:	48 8b 45 d8          	mov    -0x28(%rbp),%rax
    12b8:	ff d0                	callq  *%rax

	for (int i = 0; i < 2; i++) {
    12ba:	c7 45 cc 00 00 00 00 	movl   $0x0,-0x34(%rbp)
    12c1:	83 7d cc 01          	cmpl   $0x1,-0x34(%rbp)
    12c5:	7f 2a                	jg     12f1 <main+0x128>
		pf = (pfunction)*((long*)(*(long*)&base) + i);
    12c7:	8b 45 cc             	mov    -0x34(%rbp),%eax
    12ca:	48 98                	cltq   
    12cc:	48 8d 14 c5 00 00 00 	lea    0x0(,%rax,8),%rdx
    12d3:	00 
    12d4:	48 8d 45 e0          	lea    -0x20(%rbp),%rax
    12d8:	48 8b 00             	mov    (%rax),%rax
    12db:	48 01 d0             	add    %rdx,%rax
    12de:	48 8b 00             	mov    (%rax),%rax
    12e1:	48 89 45 d8          	mov    %rax,-0x28(%rbp)
		pf();
    12e5:	48 8b 45 d8          	mov    -0x28(%rbp),%rax
    12e9:	ff d0                	callq  *%rax
	for (int i = 0; i < 2; i++) {
    12eb:	83 45 cc 01          	addl   $0x1,-0x34(%rbp)
    12ef:	eb d0                	jmp    12c1 <main+0xf8>
	}
}
    12f1:	b8 00 00 00 00       	mov    $0x0,%eax
    12f6:	48 8b 4d f8          	mov    -0x8(%rbp),%rcx
    12fa:	64 48 33 0c 25 28 00 	xor    %fs:0x28,%rcx
    1301:	00 00 
    1303:	74 05                	je     130a <main+0x141>
    1305:	e8 a6 fd ff ff       	callq  10b0 <__stack_chk_fail@plt>
    130a:	c9                   	leaveq 
    130b:	c3                   	retq   

#endif
